package com.iplant.wms.serviceImpl.dao.asn;

import com.iplant.base.po.OutResult;
import com.iplant.base.po.Pagination;
import com.iplant.base.po.enums.ExceptionTypes;
import com.iplant.base.utils.annotation.dao.DBBaseDAO;
import com.iplant.base.utils.general.StringUtils;
import com.iplant.base.utils.sql.BaseDAO;
import com.iplant.basic.service.po.bms.BMSEmployee;
import com.iplant.wms.service.basicenum.asn.WMSReceiveStatus;
import com.iplant.wms.service.basicenum.asn.WMSReceiveType;
import com.iplant.wms.service.po.asn.WMSArrivalNotice;
import com.iplant.wms.service.po.asn.WMSArrivalNoticeItem;
import com.iplant.wms.service.po.asn.WMSReceiveBill;
import com.iplant.wms.service.po.asn.WMSReceiveBillItem;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@DBBaseDAO(WMSReceiveBill.class)
public class WMSReceiveBillDAO extends BaseDAO {


    private static WMSReceiveBillDAO Instance = null;

    public static WMSReceiveBillDAO getInstance() {
        if (Instance == null) {
            Instance = new WMSReceiveBillDAO();
        }
        return Instance;
    }

    private WMSReceiveBillDAO() {
        super("");
        this.Init();
    }

    /**
     * 由到货单及其子项，创建收货单及其子项
     *
     * @param wLoginUser        用户
     * @param wWMSArrivalNotice 到货单（包括子项）
     * @param wErrorCode        错误代码
     * @return 收货单对象（包括子项）
     */
    public WMSReceiveBill WMS_CreateFromArrival(BMSEmployee wLoginUser, WMSArrivalNotice wWMSArrivalNotice,
                                                OutResult<Integer> wErrorCode) {
        WMSReceiveBill wWMSReceiveBill = new WMSReceiveBill();
        int wParentID = 0; // 生成的主单据ID，若子单据生成过程中异常，可回滚；
        OutResult<Integer> wErrorCodeSub = new OutResult<>(0);
        // 处理子项时的ErrorCode，防止自带ErrorCode被回滚函数给复位成0，无法返回给前端的错误情况；
        try {

            if (wWMSArrivalNotice == null || wWMSArrivalNotice.ID <= 0 || StringUtils.isEmpty(wWMSArrivalNotice.Code)) {
                wErrorCode.set(ExceptionTypes.Parameter.getValue());
                return wWMSReceiveBill;
            }
            wWMSArrivalNotice.ItemList = WMSArrivalNoticeItemDAO.getInstance().WMS_GetArrivalNoticeItemList(wLoginUser,
                    wWMSArrivalNotice.ID, Pagination.getNewMaxSize(), wErrorCode);
            if (wErrorCode.Result != 0) {
                return wWMSReceiveBill;
            }

            if (wWMSArrivalNotice.ItemList == null || wWMSArrivalNotice.ItemList.size() == 0) {
                log.info("WMS_CreateFromArrival", "有效的物料清单为空。");
                wErrorCode.Result = ExceptionTypes.Parameter.getValue();
                return wWMSReceiveBill;
            }

            wWMSReceiveBill = this.WMS_SelectReceiveBill(wLoginUser, wWMSArrivalNotice.ID, wErrorCode);

            if (wWMSReceiveBill.ID > 0 && wWMSReceiveBill.ItemList != null && wWMSReceiveBill.ItemList.size() > 0) {
                return wWMSReceiveBill;
            }

            // 主单据
            wWMSReceiveBill.Name = wWMSArrivalNotice.Name;
            wWMSReceiveBill.Active = wWMSArrivalNotice.Active;
            wWMSReceiveBill.FactoryID = wWMSArrivalNotice.FactoryID;
            wWMSReceiveBill.PurchaseOrderID = wWMSArrivalNotice.PurchaseOrderID;
            wWMSReceiveBill.PurchaseOrderCode = wWMSArrivalNotice.PurchaseOrderCode;
            wWMSReceiveBill.ArrivalNoticeID = wWMSArrivalNotice.ID;
            wWMSReceiveBill.ArrivalNoticeCode = wWMSArrivalNotice.Code;
            wWMSReceiveBill.TypeID = WMSReceiveType.Notice.getValue();
            wWMSReceiveBill.CustomerID = wWMSArrivalNotice.CustomerID;
            wWMSReceiveBill.SupplierID = wWMSArrivalNotice.SupplierID;
            wWMSReceiveBill.ReceiverID = wLoginUser.ID;
            wWMSReceiveBill.ReceiverName = wLoginUser.Name;
            wWMSReceiveBill.ReceivedTime = Calendar.getInstance();
            wWMSReceiveBill.ProjectNo = wWMSArrivalNotice.ProjectNo;
            wWMSReceiveBill.ImageList = wWMSArrivalNotice.ImageList;
            wWMSReceiveBill.Status = WMSReceiveStatus.GoodsToBeReceived.getValue();
            wWMSReceiveBill.Description = wWMSArrivalNotice.Description;
            wWMSReceiveBill.setUserInfo(wLoginUser);
            // 保存主单据以获得ID给子项
            this.UpdatePo(wLoginUser, wWMSReceiveBill, wErrorCode);
            if (wErrorCode.Result != 0) {
                return wWMSReceiveBill;
            }
            if (wWMSReceiveBill == null || wWMSReceiveBill.ID <= 0) {
                return wWMSReceiveBill;
            }


            for (WMSArrivalNoticeItem wArrivalNoticeItem : wWMSArrivalNotice.ItemList) {
                WMSReceiveBillItem wReceiveBillItem = new WMSReceiveBillItem();
                wReceiveBillItem.FactoryID = wArrivalNoticeItem.FactoryID;
                wReceiveBillItem.ReceiveBillID = wWMSReceiveBill.ID;
                wReceiveBillItem.CustomerID = wArrivalNoticeItem.CustomerID;
                wReceiveBillItem.CustomerCode = wArrivalNoticeItem.CustomerCode;
                wReceiveBillItem.CustomerName = wArrivalNoticeItem.CustomerName;
                wReceiveBillItem.SupplierID = wArrivalNoticeItem.SupplierID;
                wReceiveBillItem.SupplierCode = wArrivalNoticeItem.SupplierCode;
                wReceiveBillItem.SupplierName = wArrivalNoticeItem.SupplierName;
                wReceiveBillItem.ArrivalNoticeItemID = wArrivalNoticeItem.ID;
                wReceiveBillItem.ArrivalNoticeItemCode = wArrivalNoticeItem.Code;
                wReceiveBillItem.ArrivalNoticeID = wArrivalNoticeItem.ArrivalNoticeID;
                wReceiveBillItem.PurchaseOrderItemID = wArrivalNoticeItem.PurchaseOrderItemID;
                wReceiveBillItem.PurchaseOrderItemCode = wArrivalNoticeItem.PurchaseOrderItemCode;
                wReceiveBillItem.PurchaseOrderID = wArrivalNoticeItem.PurchaseOrderID;
                wReceiveBillItem.PurchaseOrderCode = wArrivalNoticeItem.PurchaseOrderCode;
                wReceiveBillItem.MaterialID = wArrivalNoticeItem.MaterialID;
                wReceiveBillItem.MaterialCode = wArrivalNoticeItem.MaterialCode;
                wReceiveBillItem.MaterialName = wArrivalNoticeItem.MaterialName;
                wReceiveBillItem.PackageRuleID = wArrivalNoticeItem.PackageRuleID;
                wReceiveBillItem.PackageRuleCode = wArrivalNoticeItem.PackageRuleCode;
                wReceiveBillItem.PackageRuleName = wArrivalNoticeItem.PackageRuleName;
                wReceiveBillItem.ArrivalQuantity = wArrivalNoticeItem.MaterialNumber;
                wReceiveBillItem.ReceiveQuantity = 0.0; // 待用户填
                wReceiveBillItem.ReceiveUnit = wArrivalNoticeItem.ReceiveUnit;
                wReceiveBillItem.ReceiveUnitText = wArrivalNoticeItem.ReceiveUnitText;
                wReceiveBillItem.TotalVolume = wArrivalNoticeItem.TotalVolume;
                wReceiveBillItem.TotalGrossWeight = wArrivalNoticeItem.TotalGrossWeight;
                wReceiveBillItem.TotalNetWeight = wArrivalNoticeItem.TotalNetWeight;
                wReceiveBillItem.TotalPrice = wArrivalNoticeItem.TotalPrice;
                wReceiveBillItem.Description = wArrivalNoticeItem.Description;
                wReceiveBillItem.ReceiveStoreID = wArrivalNoticeItem.StoreID;
                wReceiveBillItem.ReceiveStoreName = wArrivalNoticeItem.StoreName;
                wReceiveBillItem.ReceiveStoreCode = wArrivalNoticeItem.StoreCode;


                wReceiveBillItem.ReceiveStationID = wArrivalNoticeItem.PlanStationID;
                wReceiveBillItem.ReceiveStationCode = wArrivalNoticeItem.PlanStationCode;
                wReceiveBillItem.ReceiveStationName = wArrivalNoticeItem.PlanStationName;

                wReceiveBillItem.BatchRecordID = wArrivalNoticeItem.BatchRecordID;
                wReceiveBillItem.BatchRuleCode = wArrivalNoticeItem.BatchRuleCode;
                wReceiveBillItem.BatchRecordCode = wArrivalNoticeItem.BatchRecordCode;
                wReceiveBillItem.ProductDate = wArrivalNoticeItem.ProductDate;
                wReceiveBillItem.ExpireDate = wArrivalNoticeItem.ExpireDate;
                wReceiveBillItem.StockDate = wArrivalNoticeItem.StockDate;
                wReceiveBillItem.LotAttr04 = wArrivalNoticeItem.LotAttr04;
                wReceiveBillItem.LotAttr05 = wArrivalNoticeItem.LotAttr05;
                wReceiveBillItem.LotAttr06 = wArrivalNoticeItem.LotAttr06;
                wReceiveBillItem.LotAttr07 = wArrivalNoticeItem.LotAttr07;
                wReceiveBillItem.LotAttr08 = wArrivalNoticeItem.LotAttr08;
                wReceiveBillItem.LotAttr09 = wArrivalNoticeItem.LotAttr09;
                wReceiveBillItem.LotAttr10 = wArrivalNoticeItem.LotAttr10;
                wReceiveBillItem.LotAttr11 = wArrivalNoticeItem.LotAttr11;
                wReceiveBillItem.LotAttr12 = wArrivalNoticeItem.LotAttr12;

                wReceiveBillItem.ProjectNo = wArrivalNoticeItem.ProjectNo;
                wReceiveBillItem.ProjectName = wArrivalNoticeItem.ProjectName;

                wReceiveBillItem.setUserInfo(wLoginUser);
                wWMSReceiveBill.ItemList.add(wReceiveBillItem);
            }
            // 之所以没有用BasePO的UpdatePo逐个保存，是此过程作为一个整体执行，避免中途报错无法回滚，因此带来的问题是无法自动生成Code；
            WMSReceiveBillItemDAO.getInstance().WMS_UpdateReceiveBillItemList(wLoginUser, wWMSReceiveBill.ItemList,
                    wErrorCode);

        } catch (Exception ex) {
            wErrorCode.set(ExceptionTypes.DBSQL.getValue());
            log.error("WMS_CreateFromArrival", ex);
        }
        // 如果出错了，且主单据插入成功，则删掉主单据
        if (wErrorCodeSub.Result > 0 && wParentID > 0) {
            int wDeleteResult = DeletePO(wLoginUser, wWMSReceiveBill, wErrorCode);
            wErrorCode.set(wErrorCodeSub.Result);
        }

        return wWMSReceiveBill;
    }


    public WMSReceiveBill WMS_SelectReceiveBill(BMSEmployee wLoginUser, int wArrivalNoticeID,
                                                OutResult<Integer> wErrorCode) {
        WMSReceiveBill wWMSReceiveBill = new WMSReceiveBill();

        if (wArrivalNoticeID <= 0) {
            wErrorCode.set(ExceptionTypes.Parameter.getValue());
            return wWMSReceiveBill;
        }
        Map<String, Object> wParams = new HashMap<>();
        wParams.put("ArrivalNoticeID", wArrivalNoticeID);

        return this.SelectPO(wLoginUser, wParams, wErrorCode);
    }

    public void WMS_DeleteReceiveBillList(BMSEmployee wLoginUser, int wArrivalNoticeID, OutResult<Integer> wErrorCode) {
        try {
            if (wArrivalNoticeID <= 0) {
                wErrorCode.set(ExceptionTypes.Parameter.getValue());
                return;
            }
            Map<String, Object> wParams = new HashMap<>();
            wParams.put("ArrivalNoticeID", wArrivalNoticeID);

            this.Delete(wParams, " and ID>0");

            WMSReceiveBillItemDAO.getInstance().WMS_DeleteReceiveBillItemListByArrivalNoticeID(wLoginUser,
                    wArrivalNoticeID, wErrorCode);
        } catch (Exception ex) {
            wErrorCode.set(ExceptionTypes.DBSQL.getValue());
            log.error(ex.toString());
        }
    }

    public void WMS_DeleteReceiveBillList(BMSEmployee wLoginUser, List<Integer> wArrivalNoticeIDList,
                                          OutResult<Integer> wErrorCode) {
        try {
            if (wArrivalNoticeIDList == null || wArrivalNoticeIDList.size() == 0) {
                return;
            }
            this.Delete(StringUtils.Format("ArrivalNoticeID in ()",
                    StringUtils.Join(",", wArrivalNoticeIDList)), " and ID>0 ");
        } catch (Exception ex) {
            wErrorCode.set(ExceptionTypes.DBSQL.getValue());
            log.error(ex.toString());
        }
    }


    public List<Integer> WMS_GetU8BillIDList(Calendar wStartTime, Calendar wEndTime, OutResult<Integer> wErrorCode) {
        List<Integer> wResult = new ArrayList<>();
        wErrorCode.set(0);

        try {
            String wSQL = StringUtils.Format(
                    "SELECT DISTINCT(t.U8BillID) " +
                            " FROM {0}.wms_receivebill t " +
                            " WHERE t.ID > 0 " +
                            " AND (:StartTime <= str_to_date(''2010-01-01'', ''%Y-%m-%d'') OR :StartTime <= t" +
                            ".ReceivedTime) " +
                            " AND (:EndTime <= str_to_date(''2010-01-01'', ''%Y-%m-%d'') OR :EndTime >= t" +
                            ".ReceivedTime) ",
                    super.getDBName());

            Map<String, Object> wParamMap = new HashMap<String, Object>();
            wParamMap.put("StartTime", wStartTime);
            wParamMap.put("EndTime", wEndTime);

            wSQL = this.DMLChange(wSQL);

            List<WMSReceiveBill> wWMSBillList = this.QueryForList(wSQL, wParamMap, Pagination.getMaxSize(),
                    WMSReceiveBill.class);
            wResult = wWMSBillList.stream().map(p -> p.U8BillID).distinct().collect(Collectors.toList());
        } catch (Exception ex) {
            wErrorCode.set(ExceptionTypes.DBSQL.getValue());
            log.error(ex.toString());
        }

        return wResult;
    }
}
